home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d11 / evnansi.arc / EVNANSI.ASM < prev    next >
Assembly Source File  |  1990-07-03  |  29KB  |  1,105 lines

  1.     page    66, 132
  2. ;--- evnansi.asm ----------------------------------------------------------
  3. ; New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;    evnansi.asm   - all DOS function handlers except init
  9. ;    evnansip.asm - parameter parser for ANSI escape sequences
  10. ;    evnansif.asm - ANSI command handlers
  11. ;    evnansii.asm - init DOS function handler
  12. ;
  13. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  14. ; Revision history:
  15. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  16. ; 6  july 85: split off ANSI stuff into other files, added backspace
  17. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  18. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  19. ; 9  aug 85:  added cursor position reporting
  20. ; 10 aug 85:  added output character translation
  21. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  22. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  23. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  24. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  25. ;          direct cursor positioning, takeover of BIOS write_tty,
  26. ;          noticed & squashed 2 related bugs in tab expansion
  27. ; 13 feb 86:  Squashed them again, harder
  28. ;------------------------------------------------------------------------
  29.  
  30.     include evnansid.asm    ; definitions
  31.  
  32.     ; from evnansif.asm
  33.     extrn    f_escape:near, f_in_escape:near
  34.  
  35.     ; from evnansip.asm
  36.     extrn    param_end:word, redef_end:word
  37.  
  38.     ; from evnansii.asm
  39.     extrn    dosfn0:near
  40.  
  41.     ; to evnansip.asm
  42.     public    f_loopdone
  43.     public    f_not_ansi
  44.     public    f_ansi_exit
  45.  
  46.     ; to both evnansip.asm and evnansif.asm
  47.     public    cur_x, cur_y, max_x, cur_attrib
  48.  
  49.     ; to evnansif.asm
  50.     public    xy_to_regs, get_blank_attrib
  51.     public    port_6845
  52.     public    wrap_flag
  53.     public    cur_parm_ptr
  54.     public    cur_coords, saved_coords, max_y
  55.     public    escvector, string_term
  56.     public    cpr_esc, cprseq
  57.     public    video_mode
  58.     public    lookup
  59.     public    in_g_mode
  60.  
  61.     ; to evnansii.asm
  62.     public    req_ptr, break_handler
  63.     public    int_29
  64.     if    takeBIOS
  65.     public    new_vid_bios, old_vid_bios
  66.     endif
  67.  
  68.     ; to all modules
  69.     public    xlate_tab_ptr
  70.  
  71. ;--- seg_cs is the CS: override prefix
  72. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  73. seg_cs    macro
  74.     db    2eh
  75.     endm
  76.  
  77. ;--- push_all, pop_all ------------------------------------------------
  78. ; Save/restore all user registers.
  79. push_all    macro
  80.     if    is_8088
  81.     push    ax
  82.     push    bx
  83.     push    cx
  84.     push    dx
  85.     push    bp
  86.     push    si
  87.     push    di
  88.     else
  89.     pusha
  90.     endif
  91.     endm
  92.  
  93. pop_all macro
  94.     if    is_8088
  95.     pop    di
  96.     pop    si
  97.     pop    bp
  98.     pop    dx
  99.     pop    cx
  100.     pop    bx
  101.     pop    ax
  102.     else
  103.     popa
  104.     endif
  105.     endm
  106.  
  107. keybuf    struc                ; Used in getchar
  108. len    dw    ?
  109. adr    dw    ?
  110. keybuf    ends
  111.  
  112.  
  113. ABS40    segment at 40h
  114.     org    1ah
  115. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  116. buffer_tail    dw    ?
  117.  
  118.     org    49h
  119. crt_mode    db    ?
  120. crt_cols    dw    ?
  121. crt_len     dw    ?
  122. crt_start    dw    ?
  123. cursor_posn    dw    8 dup (?)
  124. cursor_mode    dw    ?
  125. active_page    db    ?
  126. addr_6845    dw    ?
  127. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  128. crt_palette    db    ?
  129.  
  130. ;GL:11-29-88 -----------------------------------------------------------
  131. ; EGA/VGA information
  132.     org    84h
  133. crt_rows    db    ?
  134. fontsize    dw    ?
  135. egainfo        db    ?
  136. egaswitches    db    ?
  137. vgainfo        db    ?
  138. dccindex    db    ?
  139. ;GL:11-29-88 -----------------------------------------------------------
  140.     org    6ch
  141. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  142.  
  143. ABS40    ends
  144.  
  145.     page
  146.  
  147. CODE    segment byte public 'CODE'
  148. assume    cs:code, ds:code
  149.  
  150.     ; Device Driver Header
  151.  
  152.     org    0
  153.  
  154.     dd    -1            ; next device
  155.     dw    8013h            ; attributes
  156.     dw    strategy        ; request header pointer entry
  157.     dw    interrupt        ; request entry point
  158.     db    'CON     '              ; device name (8 char)
  159.  
  160.     ; Identification- in case somebody TYPEs the assembled driver
  161.     db    27, '[2J'
  162. ;GL:11-29-88 -----------------------------------------------------------
  163.     db    "EVNANSI.SYS v2.2"
  164. ;    db    "Nansi.sys v2.2"
  165. ;GL:11-29-88 -----------------------------------------------------------
  166.     ife    is_8088
  167.     db    "(80286)"
  168.     endif
  169.     db    ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  170.     db    13, 10, 26
  171.  
  172.  
  173. ;----- variable area --------------------
  174. req_ptr label    dword
  175. req_off dw    ?
  176. req_seg dw    ?
  177.  
  178. wrap_flag    db    1    ; 0 = no wrap past line end
  179. escvector    dw    0    ; state vector of ESCape sequencor
  180. video_mode    db    3    ; ROM BIOS video mode (2=BW, 3=color)
  181. max_y        db    24
  182. max_cur_x    label    word    ; used to get both max & cur at once
  183. max_x        db    79    ; line width (79 for 80x25 modes)
  184. cur_coords    label    word
  185. cur_x        db    0    ; cursor position (0 = left edge)
  186. cur_y        db    0    ;          (0 = top edge)
  187. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  188. string_term    db    0    ; either escape or double quote
  189. cur_attrib    db    7    ; current char attributes
  190. cur_page    db    0    ; current display page
  191. video_seg    dw    ?    ; segment of video card
  192. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  193. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  194. port_6845    dw    ?    ; port address of 6845 card
  195. xlate_tab_ptr    dw    ?    ; pointer to output translation table
  196.         if    takeBIOS
  197. old_vid_bios    dd    ?    ; pointer to old video bios routine
  198.         endif
  199.  
  200. brkkeybuf    db    3    ; control C
  201. fnkeybuf    db    ?    ; holds second byte of fn key codes
  202. cpr_buf     db    8 dup (?), '['
  203. cpr_esc     db    1bh    ; descending buffer for cpr function
  204.  
  205. ; following four keybufs hold information about input
  206. ; Storage order determines priority- since the characters making up a function
  207. ; key code must never be separated (say, by a Control-Break), they have the
  208. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  209. ; lowest priority.
  210.  
  211. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  212. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  213. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  214. xlatseq keybuf    <0>        ; keyboard reassignment string
  215.  
  216. ;------ xy_to_regs --------------------------------------------
  217. ; on entry: x in cur_x, y in cur_y
  218. ; on exit:  dx = chars left on line, di = address
  219. ; Alters ax, bx.
  220. xy_to_regs    proc    near
  221.     ; Find number of chars 'till end of line, keep in DX
  222.     mov    ax, max_cur_x
  223.     mov    bx, ax            ; save max_x & cur_x for next block
  224.     mov    ah, 0            ; ax = max_x
  225.     xchg    dx, ax
  226.     mov    al, bh
  227.     mov    ah, 0            ; ax = cur_x
  228.     sub    dx, ax
  229.     inc    dx            ; dx is # of chars till EOL
  230.     ; Calculate DI = current address in text buffer
  231.     mov    al, bl            ; al = max_x
  232.     inc    al
  233.     mul    cur_y
  234.     add    al, bh            ; al += cur_x
  235.     adc    ah, 0            ; AX is # of chars into buffer
  236.     add    ax, ax
  237.     xchg    di, ax            ; DI is now offset of cursor.
  238.     ret
  239. xy_to_regs    endp
  240.  
  241.  
  242. ;------- dos_fn_tab -------------
  243. ; This table is used in "interrupt" to call the routine that handles
  244. ; the requested function.
  245.  
  246. max_cmd equ    12
  247. dos_fn_tab:
  248.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  249.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  250.  
  251. ;------- strategy ----------------------------------------------------
  252. ; DOS calls strategy with a request which is to be executed later.
  253. ; Strategy just saves the request.
  254.  
  255. strategy    proc    far
  256.     mov    cs:req_off,BX
  257.     mov    cs:req_seg,ES
  258.     ret
  259. strategy    endp
  260.  
  261. ;------ interrupt -----------------------------------------------------
  262. ; This is where the request handed us during "strategy" is
  263. ; actually carried out.
  264. ; Calls one of 12 subroutines depending on the function requested.
  265. ; Each subroutine returns with exit status in AX.
  266.  
  267. interrupt    proc    far
  268.     sti
  269.     push_all            ; preserve caller's registers
  270.     push    ds
  271.     push    es
  272.  
  273.     ; Read requested function information into registers
  274.     lds    bx,cs:req_ptr
  275.     mov    al,[BX+02h]        ; al = function code
  276.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  277.     mov    cx,[BX+12h]        ; cx = input/output byte count
  278.  
  279.     cmp    al, max_cmd
  280.     ja    unk_command        ; too big, exit with error code
  281.  
  282.     xchg    bx, ax
  283.     shl    bx, 1            ; form index to table of words
  284.     mov    ax, cs
  285.     mov    ds, ax
  286.     call    word ptr dos_fn_tab[bx]
  287. int_done:
  288.     lds    bx,cs:req_ptr        ; report status
  289.     or    ax, 100h        ; (always set done bit upon exit)
  290.     mov    [bx+03],ax
  291.  
  292.     pop    ES            ; restore caller's registers
  293.     pop    DS
  294.     pop_all
  295.     ret                ; return to DOS.
  296.  
  297. unk_command:
  298.     call    badcmd
  299.     jmp    int_done
  300.  
  301. interrupt    endp
  302.  
  303. ;----- BIOS break handler -----------------------------------------
  304. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  305. ; Simply notes that a break was hit.  Flag is checked during input calls.
  306.  
  307. break_handler    proc
  308.     mov    cs:brkkey.len, 1
  309.     iret
  310. break_handler    endp
  311.  
  312.     page
  313.  
  314. ;------ badcmd -------------------------------------------------------
  315. ; Invalid function request by DOS.
  316. badcmd    proc    near
  317.     mov    ax, 813h        ; return "Error: invalid cmd"
  318.     ret
  319. badcmd    endp
  320.  
  321.  
  322. ;------ nopcmd -------------------------------------------------------
  323. ; Unimplemented or dummy function request by DOS.
  324. nopcmd    proc    near
  325.     xor    ax, ax            ; No error, not busy.
  326.     ret
  327. nopcmd    endp
  328.  
  329. ;------- dos function #4 ----------------------------------------
  330. ; Reads CX characters from the keyboard, places them in buffer at
  331. ; ES:SI.
  332. dosfn4    proc    near
  333.     jcxz    dos4done
  334.     mov    di, si
  335. dos4lp: push    cx
  336.     call    getchar
  337.     pop    cx
  338.     stosb
  339.     loop    dos4lp
  340. dos4done:
  341.     xor    ax, ax            ; No error, not busy.
  342.     ret
  343. dosfn4    endp
  344.  
  345. ;-------- dos function #5: non-destructive input, no wait ------
  346. ; One-character lookahead into the keyboard buffer.
  347. ; If no characters in buffer, return BUSY; otherwise, get value of first
  348. ; character of buffer, stuff into request header, return DONE.
  349. dosfn5    proc    near
  350.     call    peekchar
  351.     jz    dos5_busy
  352.  
  353.     lds    bx,req_ptr
  354.     mov    [bx+0Dh], al
  355.     xor    ax, ax            ; No error, not busy.
  356.     jmp    short dos5_exit
  357. dos5_busy:
  358.     MOV    ax, 200h        ; No error, busy.
  359. dos5_exit:
  360.     ret
  361.  
  362. dosfn5    endp
  363.  
  364. ;-------- dos function #6: input status --------------------------
  365. ; Returns "busy" if no characters waiting to be read.
  366. dosfn6    proc    near
  367.     call    peekchar
  368.     mov    ax, 200h        ; No error, busy.
  369.     jz    dos6_exit
  370.     xor    ax, ax            ; No error, not busy.
  371. dos6_exit:
  372.     ret
  373. dosfn6    endp
  374.  
  375. ;-------- dos function #7: flush input buffer --------------------
  376. ; Clears the IBM keyboard input buffer.  Since it is a circular
  377. ; queue, we can do this without knowing the beginning and end
  378. ; of the buffer; all we need to do is set the tail of the queue
  379. ; equal to the head (as if we had read the entire queue contents).
  380. ; Also resets all the device driver's stuffahead buffers.
  381. dosfn7    proc    near
  382.     xor    ax, ax
  383.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  384.     mov    cprseq.len, ax
  385.     mov    brkkey.len, ax
  386.     mov    xlatseq.len, ax
  387.  
  388.     mov    ax, abs40
  389.     mov    es, ax
  390.     mov    ax, es:buffer_head    ; clear queue by making the tail
  391.     mov    es:buffer_tail, ax    ; equal to the head
  392.  
  393.     xor    ax, ax            ; no error, not busy.
  394.     ret
  395. dosfn7    endp
  396.  
  397.     page
  398.     if    takeBIOS
  399. ;--- new_vid_bios -------------------------------------------
  400. ; New_vid_bios simply replaces the write_tty call.
  401. ; All other calls get sent to the old video bios.
  402. ; This gives BIOS ANSI capability.
  403. ; However, it takes away the escape character.
  404. ; If this is not desired, just tell init to not take over the vector.
  405.  
  406. new_vid_bios    proc
  407.     cmp    ah, 14
  408.     jz    nvb_write_tty
  409.     jmp    dword ptr cs:old_vid_bios
  410. nvb_write_tty:
  411.     push    cx
  412.     mov    cl, cs:cur_attrib
  413.     ; If in graphics mode, BL is new color
  414.     call    in_g_mode        ; returns carry set if text mode
  415.     jc    nvb_wt_text
  416.         mov    cs:cur_attrib, bl    ; ja?
  417. nvb_wt_text:
  418.     int    29h            ; write AL
  419.     mov    cs:cur_attrib, cl    ; restore color
  420.     pop    cx
  421.     iret
  422.  
  423. new_vid_bios    endp
  424.     endif
  425.  
  426. ;------ int_29 ----------------------------------------------
  427. ; Int 29 handles DOS quick-access putchar.
  428. ; Last device loaded with attribute bit 4 set gets accessed for
  429. ; single-character writes via int 29h instead of via interrupt.
  430. ; Must preserve all registers.
  431. ; Installed as int 29h by dosfn0 (init).
  432. int_29_buf    db    ?
  433.  
  434. int_29    proc    near
  435.     sti
  436.     push    ds
  437.     push    es
  438.     push_all
  439.     mov    cx, 1
  440.     mov    bx, cs
  441.     mov    es, bx
  442.     mov    ds, bx
  443.     mov    si, offset int_29_buf
  444.     mov    byte ptr [si], al
  445.     call    dosfn8
  446.     pop_all
  447.     pop    es
  448.     pop    ds
  449.     iret
  450. int_29    endp
  451.  
  452.     page
  453. ;------ dosfn8 -------------------------------------------------------
  454. ; Handles writes to the device (with or without verify).
  455. ; Called with
  456. ;  CX     = number of bytes to write
  457. ;  ES:SI = transfer buffer
  458. ;  DS     = CS, so we can access local variables.
  459.  
  460. dosfn8    proc    near
  461.  
  462.     mov    f_cptr_seg, es    ; save segment of char ptr
  463.  
  464.     ; Read the BIOS buffer address/cursor position variables.
  465.     mov    ax, abs40
  466.     mov    ds, ax
  467.     assume    ds:abs40
  468.  
  469.     ; Find current video mode and screen size.
  470.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  471.     mov    cs:video_mode, al
  472.     dec    ah            ; ah = max column
  473.     mov    cs:max_x, ah
  474.  
  475.     ; Find current cursor coordinates.
  476.     mov    al,active_page
  477.     cbw
  478.     add    ax,ax
  479.     xchg    bx,ax
  480.     mov    ax,cursor_posn[bx]
  481.     mov    cs:cur_coords,AX
  482.  
  483.     ; Find video buffer segment address; adjust it
  484.     ; so the offset is zero; return in AX.
  485.  
  486.     ; DS is abs40.
  487.     ; Find 6845 address.
  488.     mov    ax, addr_6845
  489.     mov    cs:port_6845, ax
  490.     ; Find video buffer address.
  491.     MOV    AX,crt_start
  492.     shr    ax, 1
  493.     shr    ax, 1
  494.     shr    ax, 1
  495.     shr    ax, 1
  496.     add    ah, 0B0h        ; assume it's a monochrome card...
  497.     CMP    cs:video_mode,07
  498.     jz    d8_gots
  499.     add    ah, 8            ; but if not mode 7, it's color.
  500. d8_gots:
  501.     push    cs
  502.     pop    ds
  503.     assume    ds:code
  504.     mov    video_seg, ax
  505.     mov    es, ax
  506.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  507.  
  508.     ; | If in graphics mode, clear old pseudocursor
  509.     call    in_g_mode
  510.     jc    d8_no_cp
  511.         call    pseudocursor    ; write block in xor
  512. d8_no_cp:
  513.     mov    bx, xlate_tab_ptr    ; get pointer to translation table
  514.  
  515.     mov    ah, cur_attrib
  516.     mov    ds, f_cptr_seg        ; get segment of char ptr
  517.     assume    ds:nothing
  518.     cld                ; make sure we'll increment
  519.  
  520.     ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  521.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  522.     ; At that speed, it takes 32 milliseconds to fill a screen.
  523.  
  524.     ; Get a character, put it on the screen, repeat 'til end of line
  525.     ; or no more characters.
  526.     jcxz    f_loopdone        ; if count = 0, we're already done.
  527.     cmp    cs:escvector, 0     ; If in middle of an escape sequence,
  528.     jnz    f_in_escapex        ; jump to escape sequence handler.
  529.  
  530. f_tloop:; | If in graphics mode, jump to alternate loop
  531.     ; | What a massive kludge!  A better approach would have been
  532.     ; | to collect characters for a "write n chars" routine
  533.     ; | which would handle both text and graphics modes.
  534.     call    in_g_mode
  535.     jc    f_t_cloop
  536.     jmp    f_g_cloop
  537.  
  538. f_t_cloop:
  539.     LODSB                ; get char! (al = ds:[si++])
  540.     cmp    al, 28            ; is it a control char?
  541.     jb    f_control        ;  maybe...
  542. f_t_nctl:
  543.     seg_cs
  544.     xlat
  545.     STOSW                ; Put Char! (es:[di++] = ax)
  546.     dec    dx            ; count down to end of line
  547.     loopnz    f_t_cloop        ; and go back for more.
  548.     jz    f_t_at_eol        ; at end of line; maybe do a crlf.
  549.     jmp    short f_loopdone
  550.  
  551. f_looploop:
  552. f_ansi_exit:                ; in case we switched into
  553.     loopnz    f_tloop         ; a graphics mode
  554. f_t_at_eol:
  555.     jz    f_at_eol
  556.  
  557. f_loopdone:
  558.  
  559.     ;--------- All done with write request -----------
  560.     ; DI is cursor address; cursor position in cur_y, dl.
  561.     mov    ax, cs
  562.     mov    ds, ax            ; get our segment back
  563.     assume    ds:code
  564.  
  565.     ; Restore cur_x = max_x - dx + 1.
  566.     mov    al, max_x
  567.     inc    al
  568.     sub    al, dl
  569.     mov    cur_x, al
  570.     ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  571.     call    set_pseudocursor
  572.     ; Return to DOS.
  573.     xor    ax, ax            ; No error, not busy.
  574.     ret
  575.  
  576.     ;---- handle control characters ----
  577.     ; Note: cur_x is not kept updated in memory, but can be
  578.     ; computed from max_x and dx.
  579.     ; Cur_y is kept updated in memory.
  580. f_control:
  581.     cmp    al, 27            ; Is it an escape?
  582.     jz    f_escapex
  583.     cmp    al, 13            ; carriage return?
  584.     jz    f_cr
  585.     cmp    al, 10            ; line feed?
  586.     jz    f_lf
  587.     cmp    al, 9            ; tab?
  588.     jz    f_tabx
  589.     cmp    al, 8            ; backspace?
  590.     jz    f_bs
  591.     cmp    al, 7            ; bell?
  592.     jz    f_bell
  593.     jmp    f_nctl            ; then it is not a control char.
  594.  
  595. f_tabx: jmp    f_tab
  596. f_escapex:
  597.     jmp    f_escape
  598. f_in_escapex:
  599.     jmp    f_in_escape
  600.  
  601. f_bs:    ;----- Handle backspace -----------------
  602.     ; Moves cursor back one space without erasing.    No wraparound.
  603.     cmp    dl, cs:max_x        ; wrap around to previous line?
  604.     ja    fbs_wrap        ; yep; disallow it.
  605.     dec    di            ; back up one char & attrib,
  606.     dec    di
  607.     inc    dx            ; and note one more char left on line.
  608. fbs_wrap:
  609.     jmp    f_looploop
  610.  
  611. f_bell: ;----- Handle bell ----------------------
  612.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  613.     call    beep
  614.     or    al, al            ; clear z
  615. ; old (more portable) version:
  616. ;    mov    ax, 0e07h        ; "write bell to tty simulator"
  617. ;    mov    bx, 0            ; "page zero, color black"
  618. ;    int    10h            ; call BIOS
  619. ;    mov    ah, cs:cur_attrib    ; restore current attribute
  620. ;    mov    bx, cs:xlate_tab_ptr    ; restore translate table address
  621. ;    or    al, al            ; al still 7; this clears Z.
  622.     jmp    f_looploop        ; Let main loop decrement cx.
  623.  
  624. f_cr:    ;----- Handle carriage return -----------
  625.     ; di -= cur_x<<1;        set di= address of start of line
  626.     ; dx=max_x+1;            set bx= chars left in line
  627.     mov    al, cs:max_x
  628.     inc    al
  629.     sub    al, dl            ; Get cur_x into ax.
  630.     mov    ah, 0
  631.     sub    di, ax
  632.     sub    di, ax
  633.     mov    dl, cs:max_x        ; Full line ahead of us.
  634.     inc    dx
  635.     mov    ah, cs:cur_attrib    ; restore current attribute
  636.     or    al, 1            ; clear z
  637.     jmp    f_looploop        ; and let main loop decrement cx
  638.  
  639. f_at_eol:
  640.     ;----- Handle overrunning right end of screen -------
  641.     ; cx++;             compensate for double loop
  642.     ; if (!wrap_flag) { dx++; di-=2; }
  643.     ; else do_crlf;
  644.     inc    cx
  645.     test    cs:wrap_flag, 1
  646.     jnz    feol_wrap
  647.         dec    di
  648.         dec    di
  649.         inc    dx
  650.         jmp    f_looploop
  651. feol_wrap:
  652.     ; dx=max_x+1;            set bx= chars left in line
  653.     ; di -= 2*(max_x+1);
  654.     ; do_lf
  655.     mov    dl, cs:max_x
  656.     inc    dx
  657.     sub    di, dx
  658.     sub    di, dx
  659.     ; fall thru to line feed routine
  660.  
  661. f_lf:    ;----- Handle line feed -----------------
  662.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  663.     ; else { cur_y++; di += max_x<<1;    else increment Y
  664.  
  665.     mov    al, cs:max_y
  666.     cmp    cs:cur_y, al
  667.     jb    flf_noscroll
  668.         call    scroll_up        ; preserves bx,cx,dx,si,di
  669.         jmp    short flf_done
  670. flf_noscroll:
  671.     inc    cs:cur_y
  672.     mov    al, cs:max_x
  673.     mov    ah, 0
  674.     inc    ax
  675.     add    ax, ax
  676.     add    di, ax
  677. flf_done:
  678.     mov    ah, cs:cur_attrib        ; restore current attribute
  679.     or    al, 1            ; clear z
  680.     jmp    f_looploop        ; and let main loop decrement cx
  681.  
  682. f_tab:    ;----- Handle tab expansion -------------
  683.     ; Get cur_x into al.
  684.     mov    al, cs:max_x
  685.     inc    al
  686.     sub    al, dl
  687.     ; Calculate number of spaces to output.
  688.     push    cx            ; save cx
  689.     mov    ch, 0
  690.     mov    cl, al            ; get zero based x coordinate
  691.     and    cl, 7
  692.     neg    cl
  693.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  694.     sub    dx, cx            ; update chars-to-eol, maybe set z
  695.     pushf                ; || save Z for main loop
  696.     ; ah is still current attribute.  Move CX spaces to the screen.
  697.     mov    al, ' '
  698.     call    in_g_mode        ; | graphics mode
  699.     jnc    f_tab_putc        ; |
  700.     REP    STOSW
  701.     popf                ; || restore Z flag for main loop test
  702.     pop    cx            ; restore cx
  703.     jmp    f_looploop        ; Let main loop decrement cx.
  704.  
  705. ;--------------- graphics mode support -----------------------
  706.  
  707. f_tab_putc:    ; graphics mode- call putc to put the char
  708.     add    dx, cx            ; move back to start of tab
  709. f_tp_lp:
  710.     call    putchar
  711.     dec    dx            ; go to next cursor position
  712.     loop    f_tp_lp
  713.     popf                ; Z set if wrapped around EOL
  714.     pop    cx
  715.     jmp    f_looploop
  716.  
  717. ;---- in_g_mode -------------
  718. ; Returns Carry set if not in a graphics mode.
  719. ; Preserves all registers.
  720.  
  721. in_g_mode    proc    near
  722.     cmp    cs:video_mode, 4
  723.     jb    igm_stc
  724.     cmp    cs:video_mode, 7
  725.     jz    igm_stc
  726.     clc
  727.     ret
  728. igm_stc:
  729.     stc
  730.     ret
  731. in_g_mode    endp
  732.  
  733. ;---- Where to go when a character turns out not to be special
  734. f_nctl:
  735. f_not_ansi:
  736.     call    in_g_mode
  737.     jnc    f_g_nctl        ; graphics mode
  738. f_jmptnctl:
  739.     jmp    f_t_nctl        ; text mode
  740.  
  741. ;---- Alternate main loop for graphics mode ----
  742. f_g_cloop:
  743.     LODSB                ; get char! (al = ds:[si++])
  744.     cmp    al, 28            ; is it a control char?
  745.     jb    f_g_control        ;  maybe...
  746. f_g_nctl:
  747.     seg_cs
  748.     xlat
  749.     call    putchar
  750.     dec    dx            ; count down to end of line
  751.     loopnz    f_g_cloop        ; and go back for more.
  752.     jz    f_g_at_eol        ; at end of line; maybe do a crlf.
  753.     jmp    f_loopdone
  754.  
  755. f_g_control:    jmp    f_control
  756. f_g_at_eol:    jmp    f_at_eol
  757.  
  758. ;---- putchar ------------------------------------------------
  759. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  760. ; On entry, registers set up as per xy_to_regs.
  761. ; Preserves all registers.
  762. putchar proc    near
  763.     push    dx
  764.     push    cx
  765.     push    bx
  766.     push    ax
  767.     ; 1. Set cursor position.
  768.     mov    al, cs:max_x
  769.     inc    al
  770.     sub    al, dl
  771.     mov    cs:cur_x, al
  772.     mov    dx, cs:cur_coords    ; get X & Y into DX
  773.     xor    bx, bx            ; choose dpy page 0
  774.     mov    ah, 2            ; chose "Set Cursor Position"
  775.     int    10h            ; call ROM BIOS
  776.     ; 2. Write char & attribute.
  777.     mov    cx, 1
  778.     pop    ax            ; get char in AL
  779.     push    ax
  780.     mov    bl, ah            ; attribute in BL
  781.     mov    bh, 0
  782.     mov    ah, 9
  783.     int    10h
  784.     pop    ax
  785.     pop    bx
  786.     pop    cx
  787.     pop    dx
  788.     ret
  789. putchar endp
  790.  
  791. ;---- set_pseudocursor ------------
  792. ; If in graphics mode, set pseudocursor, else set real cursor.
  793. ; Destroys DS!!!!
  794.  
  795. set_pseudocursor    proc    near
  796.     call    in_g_mode
  797.     jnc    pseudocursor
  798. ; old (more portable, but slower) version
  799. ;    mov    dx, cur_coords        ; get X & Y into DX
  800. ;    xor    bx, bx            ; choose dpy page 0
  801. ;    mov    ah, 2            ; chose "Set Cursor Position"
  802. ;    int    10h            ; call ROM BIOS
  803.  
  804.     ; Write directly to 6845 cursor address register.
  805.     mov    bx, di
  806.     shr    bx, 1            ; convert word index to byte index
  807.  
  808.     mov    dx, port_6845
  809.     mov    al, 0eh
  810.     out    dx, al
  811.  
  812.     jmp    $+2
  813.     inc    dx
  814.     mov    al, bh
  815.     out    dx, al
  816.  
  817.     jmp    $+2
  818.     dec    dx
  819.     mov    al, 0fh
  820.     out    dx, al
  821.  
  822.     jmp    $+2
  823.     inc    dx
  824.     mov    al, bl
  825.     out    dx, al
  826.  
  827.     ; Set cursor position in low memory.
  828.     assume    ds:abs40
  829.     mov    ax, abs40
  830.     mov    ds, ax
  831. ; Does anybody ever use anything but page zero?
  832. ;    mov    al,active_page
  833. ;    cbw
  834. ;    add    ax,ax
  835. ;    xchg    bx,ax
  836.     mov    ax, cs:cur_coords
  837.     mov    cursor_posn,ax
  838.     ret
  839.  
  840.     assume    ds:code
  841. set_pseudocursor    endp
  842.  
  843.  
  844. ;---- pseudocursor --------------------------------------------------
  845. ; Writes a color 15 block in XOR at the current cursor location.
  846. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  847. ; Should be disableable- the pseudocursor slows down single-char
  848. ; writes by a factor of three.
  849. pseudocursor    proc    near
  850.     mov    ax, 8f16h    ; xor, color 15, ^V (small block)
  851.     call    putchar
  852.     ret
  853. pseudocursor    endp
  854.  
  855. ;--------------- end of graphics mode support --------------------
  856.  
  857. dosfn8    endp
  858.  
  859. ;--- get_blank_attrib ------------------------------------------------
  860. ; Determine new attribute and character for a new blank region.
  861. ; Use current attribute, just disallow blink and underline.
  862. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  863. ; Returns result in AH, preserves all other registers.
  864. get_blank_attrib    proc    near
  865.     mov    ah, 0
  866.     call    in_g_mode
  867.     jnc    gb_aok            ; if graphics mode, 0 is bkgnd
  868.  
  869.     mov    ah, cs:cur_attrib
  870.     and    ah, 7fh         ; disallow blink
  871.     cmp    cs:video_mode, 7    ; monochrome?
  872.     jnz    gb_aok
  873.         cmp    ah, 1        ; underline?
  874.         jnz    gb_aok
  875.         mov    ah, 7        ; yep- set it to normal.
  876. gb_aok: ret
  877. get_blank_attrib    endp
  878.  
  879.  
  880. ;---- scroll_up ---------------------------------------------------
  881. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  882. ; Moves screen up 1 line, fills the last line with blanks.
  883. ; Attribute of blanks is the current attribute sans blink and underline.
  884.  
  885. scroll_up    proc    near
  886.     push    ax
  887.     push    bx
  888.     push    cx
  889.     push    dx
  890.  
  891.     call    get_blank_attrib
  892.     mov    bh, ah            ; color to use on new blank areas
  893.     mov    al, 1            ; AL is number of lines to scroll.
  894.     mov    ah, 6            ; BIOS: scroll up
  895.     mov    cl, 0            ; upper-left-x of data to scroll
  896.     mov    ch, 0            ; upper-left-y of data to scroll
  897.     mov    dl, cs:max_x        ; lower-rite-x
  898.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  899.     int    10h            ; call BIOS to scroll a rectangle.
  900.  
  901.     pop    dx
  902.     pop    cx
  903.     pop    bx
  904.     pop    ax
  905.     ret
  906. scroll_up    endp
  907.  
  908. ;---- lookup -----------------------------------------------
  909. ; Called by getchar, peekchar, and key to see if a given key has
  910. ; been redefined.
  911. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  912. ; Returns with Z cleared if no redefinition; otherwise,
  913. ; Z is set, SI points to redefinition string, CX is its length.
  914. ; Preseves AL, all but CX and SI.
  915. ; Redefinition table organization:
  916. ;  Strings are stored in reversed order, first char last.
  917. ;  The word following the string is the character to be replaced;
  918. ;  the next word is the length of the string sans header.
  919. ; param_end points to the last byte used by the parameter buffer;
  920. ; redef_end points to the last word used by the redef table.
  921.  
  922. lookup    proc    near
  923.     mov    si, redef_end        ; Start at end of table, move down.
  924.     or    al, al
  925.     jz    lu_lp
  926.     mov    ah, 0            ; clear extraneous scan code
  927. lu_lp:    cmp    si, param_end
  928.     jbe    lu_notfound        ; If below redef table, exit.
  929.     mov    cx, [si]
  930.     cmp    ax, [si-2]        ; are you my mommy?
  931.     jz    lu_gotit
  932.     sub    si, 4
  933.     sub    si, cx            ; point to next header
  934.     jmp    lu_lp
  935. lu_notfound:
  936.     or    si, si            ; clear Z
  937.     jmp    short lu_exit
  938. lu_gotit:
  939.     sub    si, 2
  940.     sub    si, cx            ; point to lowest char in memory
  941.     cmp    al, al            ; set Z
  942. lu_exit:
  943.     ret
  944. lookup    endp
  945.  
  946. ;---- searchbuf --------------------------------------------
  947. ; Called by getchar and peekchar to see if any characters are
  948. ; waiting to be gotten from sources other than BIOS.
  949. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  950. searchbuf    proc    near
  951.     ; Search the stuffahead buffers.
  952.     mov    cx, 4            ; number of buffers to check for chars
  953.     mov    bx, offset fnkey - 4
  954. sbloop: add    bx, 4            ; point to next buffer record
  955.     mov    si, [bx].len
  956.     or    si, si            ; empty?
  957.     loopz    sbloop            ; if so, loop.
  958.     ret
  959. searchbuf    endp
  960.  
  961. ;---- getchar -----------------------------------------------
  962. ; Returns AL = next char.
  963. ; Trashes AX, BX, CX, BP, SI.
  964. getchar proc    near
  965. gc_searchbuf:
  966.     ; See if any chars are waiting in stuffahead buffers.
  967.     call    searchbuf
  968.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  969.     ; A nonempty buffer was found.
  970.     dec    [bx].len
  971.     dec    si
  972.     mov    bp, [bx].adr        ; get pointer to string
  973.     mov    al, byte ptr ds:[bp][si]; get the char
  974.     ; Recognize function key sequences, move them to highest priority
  975.     ; queue.
  976.     sub    si, 1            ; set carry if si=0
  977.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  978.     cmp    bx, offset fnkey
  979.     jz    gc_nofnkey        ; already highest priority -> done.
  980.     or    al, al
  981.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  982.     ; Found a function key; move it to highest priority queue.
  983.     dec    [bx].len
  984.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  985. gc_fnkey:
  986.     mov    fnkey.len, 1
  987.     mov    fnkeybuf, ah        ; save it.
  988. gc_nofnkey:
  989.     ; Valid char in AL.  Return with it.
  990.     jmp    short gcdone
  991.  
  992. gc_trykbd:
  993.     ; Actually get a character from the keyboard.
  994.     mov    ah, 0
  995.     int    16h            ; BIOS returns with char in AX
  996.     ; If it's Ctrl-break, it has already been taken care of.
  997.     or    ax, ax
  998.     jz    gc_trykbd
  999.  
  1000.     ; Look in the reassignment table to see if it needs translation.
  1001.     call    lookup            ; Z=found; CX=length; SI=ptr
  1002.     jnz    gc_noredef
  1003.     ; Okay; set up the reassignment, and run thru the translation code.
  1004.     mov    xlatseq.len, cx
  1005.     mov    xlatseq.adr, si
  1006.     jmp    gc_searchbuf
  1007. gc_noredef:
  1008.     ; Is it a function key?
  1009.     cmp    al, 0
  1010.     jz    gc_fnkey        ; yep- special treatment.
  1011. gcdone: ret    ; with character in AL.
  1012.  
  1013. getchar endp
  1014.  
  1015. ;---- peekchar -----------------------------------------------
  1016. ; Returns Z if no character ready, AL=char otherwise.
  1017. ; Trashes AX, BX, CX, BP, SI.
  1018. peekchar    proc    near
  1019. pc_searchbuf:
  1020.     call    searchbuf
  1021.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1022.     ; A nonempty buffer was found.
  1023.     dec    si
  1024.     mov    bp, [bx].adr        ; get pointer to string
  1025.     mov    al, byte ptr ds:[bp][si]; get the char
  1026.     ; Valid char from buffer in AL.  Return with it.
  1027.     jmp    short pcdone
  1028. pc_trykbd:
  1029.     ; Actually peek at the keyboard.
  1030.     mov    ah, 1
  1031.     int    16h            ; BIOS returns with char in AX
  1032.     jz    pcexit
  1033.     ; If it's control-break, it's already been taken care of.
  1034.     or    ax, ax
  1035.     jnz    pc_notbrk
  1036.     mov    ah, 0
  1037.     int    16h            ; so get rid of it!
  1038.     jmp    short pc_trykbd
  1039. pc_notbrk:
  1040.     ; Look in the reassignment table to see if it needs translation.
  1041.     call    lookup            ; Z=found; CX=length; SI=ptr
  1042.     jnz    pcdone            ; Nope; just return the char.
  1043.     ; Okay; get the first code to be returned.
  1044.     add    si, cx
  1045.     mov    al, [si-1]
  1046. pcdone: or    ah, 1            ; NZ; char ready!
  1047. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1048. peekchar    endp
  1049.  
  1050. ;---- beep ------------------------------------------------------
  1051. ; Beep speaker; period given by beep_div, duration by beep_len.
  1052. ; Preserves all registers.
  1053.  
  1054. beep_div    dw    1300        ; fairly close to IBM beep
  1055. beep_len    dw    3        ; 3/18 sec- shorter than IBM
  1056.  
  1057. beep    proc    near
  1058.     push_all
  1059.  
  1060.     mov    al, 10110110b        ; select 8253
  1061.     mov    dx, 43h         ; control port address
  1062.     out    dx, al
  1063.     dec    dx            ; timer 2 address
  1064.     mov    ax, cs:beep_div
  1065.     jmp    $+2
  1066.     out    dx, al            ; low byte of divisor
  1067.     xchg    ah, al
  1068.     jmp    $+2
  1069.     out    dx, al            ; high byte of divisor
  1070.     mov    dx, 61h
  1071.     jmp    $+2
  1072.     in    al, dx            ; get current value of control bits
  1073.     push    ax
  1074.     or    al, 3
  1075.     jmp    $+2
  1076.     out    dx, al            ; turn speaker on
  1077.  
  1078.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1079.     push    es
  1080.     mov    ax, abs40
  1081.     mov    es, ax
  1082.     assume    es:abs40
  1083.     mov    bx, timer_low
  1084.     add    bx, cs:beep_len
  1085.     mov    cx, -1            ; emergency, in case clock dead
  1086. beeplp: mov    ax, timer_low
  1087.     cmp    ax, bx
  1088.     jg    beepover
  1089.     loop    beeplp
  1090. beepover:
  1091.     pop    es
  1092.     assume    es:code
  1093.  
  1094.     ; Turn off speaker
  1095.     pop    ax
  1096.     and    al, not 3        ; turn speaker off
  1097.     out    dx, al
  1098.     pop_all
  1099.     ret
  1100. beep    endp
  1101.  
  1102. CODE    ends
  1103.  
  1104.     end                ; of evnansi.asm
  1105.